from sqlalchemy import delete, select
from sqlalchemy.exc import DBAPIError, IntegrityError
from sqlalchemy.orm import joinedload

from dc_shop.core.exceptions.common import RepoError
from dc_shop.core.exceptions.user import UserCartAlreadyExist
from dc_shop.core.models.dto.cart import CartDTO
from dc_shop.database.converters.cart import convert_db_model_to_cart_dto
from dc_shop.database.exception_mapper import exception_mapper
from dc_shop.database.models import Cart, CartProduct, User
from dc_shop.database.repositories.base import SQLAlchemyRepo


class CartReaderImpl(SQLAlchemyRepo):
    @exception_mapper
    async def get_cart_by_user_id(self, user_tg_id: int) -> CartDTO | None:
        """Получаем корзину пользователя"""

        cart: Cart | None = await self._session.scalar(
            select(Cart)
            .join(Cart.user)
            .where(
                User.telegram_id == user_tg_id,
            )
            .options(joinedload(Cart.products))
        )
        if not cart:
            return None

        return convert_db_model_to_cart_dto(cart)


class CartRepoImpl(SQLAlchemyRepo):
    @exception_mapper
    async def acquire_cart_by_user_id(self, user_tg_id: int) -> Cart | None:
        """Получаем корзину пользователя"""

        cart: Cart | None = await self._session.scalar(
            select(Cart)
            .join(Cart.user)
            .where(
                User.telegram_id == user_tg_id,
            )
            .options(joinedload(Cart.products))
        )

        if not cart:
            return None

        return cart

    @exception_mapper
    async def delete_cart_items(self, cart_id: int) -> None:
        """Удаляем товары, связанные с корзиной"""

        await self._session.execute(delete(CartProduct).where(CartProduct.cart_id == cart_id))

    @exception_mapper
    async def create_cart(self, cart: Cart) -> None:
        """Создаем корзину"""

        self._session.add(cart)
        try:
            await self._session.flush((cart,))
        except IntegrityError as err:
            self._parse_error(err)

    @exception_mapper
    async def update_cart(self, cart: Cart) -> None:
        """Обновляем корзину"""

        try:
            await self._session.merge(cart)
        except IntegrityError as err:
            self._parse_error(err)

    @staticmethod
    def _parse_error(err: DBAPIError) -> None:
        match err.__cause__.__cause__.constraint_name:  # type: ignore
            case "pk_cart":
                raise UserCartAlreadyExist from err
            case "uq_cart_user_id":
                raise UserCartAlreadyExist from err
            case _:
                raise RepoError from err
